<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>note2</title>
</head>
<style>
    html, body{
        height: 100%;
        margin: 0;
    }
</style>
<body>
<script>
    /* 1.parseInt */
    ;(function () {
        return

        var result = ['1', '2', '3'].map(parseInt)
        console.log('result', result) // [1, NaN, NaN]

        // map函数结构：
        // array.map(function(item, index, array){}, this值)

        // parseInt(string, radix)
        // string 要被解析的字符串
        // radix 要解析的数字的基数： 省略或0 --> 10; 0x或0X --> 16

        // 相当于
        var result1 = parseInt('1', 0)
        var result2 = parseInt('2', 1)
        var result3 = parseInt('3', 2)

        // console.log(result1)
        // console.log(result2)
        // console.log(result3)

    })()

    /* 2.reduce */
    ;(function () {
        return

        // [].reduce(Math.pow) // 报错

        // array.reduce(function(pre, item, index, array){}, pre)

        var result = [3, 2, 1].reduce(Math.pow)
        console.log('result', result) // 9

        var result1 = [5].reduce(Math.pow)
        console.log('result1', result1)

        // 如果没有写pre, 数组第一项是pre
        var result2 = [5, 2, 3].reduce(function (pre, item, index, array) {
            console.log('pre', pre, 'item', item, 'index', index, array)
        })
    })()

    /* 3.数组 */
    ;(function () {
        return

        var arr = [1, 2, 3]
        arr[10] = '哈哈哈'
        var result = arr.filter(function (item) {
            return item === undefined
        })
        console.log('result', result) // []
    })()

    /* 4. 深浅拷贝 */
    ;(function () {
        return

        let a = {
            aa: 1,
            bb: 2,
            cc: 3,
            dd: {
                ee: 5,
            },
            ff: {
                gg: 6,
            }
        };
        let d = JSON.parse(JSON.stringify(a));//深复制包含子对象
        let c = {...a};//拷贝一层但不包含子对象，子对象还是浅拷贝
        let b = a;//浅拷贝
        // b.bb = 22;
        c.cc = 33;
        c.dd.ee = 55;
        // d.ff.gg = 66;
        console.log(a);
        // console.log(b);
        console.log(c);
        // console.log(d);
    })()

    /* 5.数组拷贝 */
    ;(function () {
        return

        var a = [1, 2, [3, 4]]
        var d = JSON.parse(JSON.stringify(a))
        var c = [...a] //同对象一样，...的只能对最外层数组进行深复制，二维数组还是浅复制
        // d[2][0] = 999
        c[0] = 100
        c[2][0] = 999
        console.log(a)
        // console.log(d)
        console.log(c)
    })()

    /* 6.Set */
    // 特点： 和数组相比，没有重复的值
    ;(function () {
        return

        var arr = [1, 1, 2, 2, 3, 3]
        var result = new Set(arr)
        console.log(result, typeof result, [...result]) // [1, 2, 3]

        var obj = {name: 'xiaoqiang', age: 18}
        // console.log([...obj]) // 报错

        var set1 = new Set([NaN, NaN])
        var set2 = new Set([{}, {}])
        console.log(set1, set1.size) // 1 --- 在Set内部，两个NaN是相等的
        console.log(set2, set2.size) // 2

        var obj1= {}
        var obj2 = obj1
        console.log(new Set([obj1, obj2])) // size为1
    })()

    /* 7.Map */
    // 特点： 和对象比，键不局限于字符串，任何类型（包括对象）都可以当键
    ;(function () {
        return

        var map = new Map()
        var obj = {name: 'qiang'}
        map.set(obj, '一个人')
        console.log(map.get(obj)) // '一个人'
        map.delete(obj)
        console.log(map.get(obj)) // undefined

    })()

    /* 8. 模拟trim() */
    ;(function () {
        return

        function myTrim(str) {
            var reg = /^\s+|\s+$/g
            return str.replace(reg, '')
        }

        var result = myTrim('     我   好帅呀   ')
        console.log(result)
    })()

    /* 9. defineProperty */
    ;(function () {
        return

        var obj = {}
        Object.defineProperty(obj, 'newKey', {})
        // Object.defineProperty(obj, 'newKey', { writable: true })
        console.log('newKey', obj.newKey) // undefined

        // 赋值不起作用
        obj.newKey = 'hi'
        console.log('newKey', obj.newKey) // undefined

        // 重新用defineProperty改变它的值会报错
        // Object.defineProperty(obj, 'newKey', {
        //     value: 'hello'
        // })

        // 原因:
        // configurable属性默认为false, 不能修改; writable默认false, 不能被重写

    })()

    /* defineProperty */
    ;(function () {
        return

        var obj = {}
        Object.defineProperty(obj, 'newKey', {
            set: function () {},
            get: function () {},
            // value: 'hello',
            // writable: true,
            enumerable: true,
            configurable: true
        })
    //    设置set或get之后，就不能再设置value和writable
    })()

    /* 10. arguments */
    ;(function () {
        return

        function foo(name) {
            console.log(name)
            console.log(arguments)
            console.log(Object.prototype.toString.call(arguments)) // [object Arguments]
            console.log([...arguments])
            console.log(Object.prototype.toString.call([...arguments])) // [object Array]
            console.log({...arguments})
            console.log(Object.prototype.toString.call({...arguments}))

            var arr = [1, 9, 9]
            console.log({...arr}) // {0: 1, 1: 9, 2: 9}
            console.log([...arr])
            var obj = {name: '小强', age: 18}
            // console.log([...obj]) // 报错
        }
        foo('小强')
    })()


    /* 11. call */
    //    （手动实现js方法系列）

    ;(function () {
        return

        function sayPerson(name) {
            console.log(name + this.age + '岁')
        }
        sayPerson('小米')
        sayPerson.call({age: 18}, '小强')

    //    实现一个call函数
        Function.prototype.myCall = function (context) {
            // myCall的第一个参数即指定的上下文（{age: 12}）
            // 这里的this相当于“这儿”的意思，“这个地方”
            var context = context || window
            // myCall的this即调用它的函数（sayPerson），这里将这个函数临时添加成context的方法
            context.fn = this // 这里的this相当于“这个”的意思，“这个函数”
            // 取出函数执行参数（'小红'）
            var args = [...arguments].slice(1)
            // 让函数以context的方法的方式执行，则函数的this为context
            var result = context.fn(...args)
            // 删除为context加上的方法
            delete context.fn
            // 返回执行的return
            return result
        }
        sayPerson.myCall({age: 12}, '小红')
    })()

    /* 12. apply */
    //    （手动实现js方法系列）

    ;(function () {
        return

        function sayPerson(name, sex) {
            console.log(`此${sex + name + this.age}岁`)
        }
        sayPerson('小米', '男')
        sayPerson.apply({age: 100}, ['小明', '男'])

        Function.prototype.myApply = function (context) {
            var context = context || window
            context.fn = this
            var arg = arguments[1] || []
            var result = context.fn(...arg) // arg是一个数组，需要展开
            delete context.fn
            return result
        }
        sayPerson.myApply({age: 12}, ['小红', '女'])
    })()

    /* 13. bind */
    //    （手动实现js方法系列）

    ;(function () {
        return

        function sayPerson(name, sex) {
            console.log(`此${sex + name + this.age}岁`)
        }
        sayPerson('小米', '男')
        sayPerson.bind({age: 333})('小明', '男男')

        //    实现一个bind函数
        Function.prototype.myBind = function (context) {
            if(typeof this !== 'function'){
                throw new TypeError('Error')
            }
            // 保存该函数
            var _this = this
        //    保存参数（通过bind传递的）
            var args = [...arguments].slice(1)
            // 返回一个函数（这是一个闭包）
            return function F() {
                return _this.apply(context, args.concat(...arguments)) // 这里的...arguments是调用返回的F函数时传递的
            }
        }
    })()


    /* 14. 原型链 */
    ;(function () {
        return

        /*
        *  原型链的作用：
        *    继承。
        *  1.每次new实例都创建原型链的作用：
        *    灵活。
        *  2.原型链最终集合到Object.prototype的作用：
        *    稳定。
        *
        *  推测：
        *  Function创建实例的时，就会执行：
        *  1.  （灵活）实例.__proto__ = 创建者.prototype 的同时：
        *  2.  （稳定）实例.prototype.__proto__ = Object.prototype （如果是函数对象）
        *       即： prototype的创建者是Object(其实是Object对 规则1 的使用)
        *  这两条规则。
        *
        *  困境：
        *  Function想要创建Object实例的时候，自身没有prototype;
        *          想要创建自身prototype的时候，有需要Object。
        *  解决：
        *  Function先创建一个临时的prototype，
        *  再创建Object，
        *  创建的时候，根据 实例.prototype.__proto__ = Object.prototype （如果是函数对象）这条规则，
        *  因为Object还不存在，所以Object也就不会为自己的原型创建原型。
        *  Objec就绪，为了让自己的prototype和成为Object的实例，
        *  Function就又创建了一次自己，因此
        *  在 Function.prototype.__proto__ === Object.prototype 的同时，
        *  Function.__proto__ === Function.prototype
        *  即Function是自己的实例。
        * */
        
    })()

    /* 15. 解构赋值 */
    ;(function () {
        return

        var obj = {title: '啦啦啦', age: 18}
        // var obj = { age: 18}
        var { title: name = '路人甲', age = 10, sex = 'man' } = obj || {}
        console.log('name:', name, 'age:', age, 'sex:', sex)
    })()

    /* 16. 缓存 */
    ;(function () {
        return

        var addTen = (function() {
            var cache = {}
            return function (num) {
                console.log('cache', cache)
                if(!cache[num]){
                    cache[num] = 10 + num
                }
                return cache[num]
            }
        })()
        console.log(addTen(2))
        console.log(addTen(2))
        console.log(addTen(3))
        console.log(addTen(4))
    })()

    /* 17.数组去重 */
    ;(function () {
        return

        var arr = [1, 2, 3, 3, 3, 4, 4, 5]
        var arr1 = [...new Set(arr)]
        console.log(arr1)
    })()

    /* 18. Set和Map */
    ;(function () {
        return
        // Set
        var arr = [1, 2, 3, 3, 3, 4, 4, 5]

        //    Set本身是一个构造函数，用来生成Set数据结构。
        //    它类似于数组，但是成员是唯一的、不重复的。

        const s = new Set()
        arr.forEach(x => {
            s.add(arr)
        })
        console.log('s', s)

    //    Map
        var m = new Map()
        m.set('hsq', '帅哥')
        m.set('lmx', '美女')
        console.log('m', m)
        console.log('m.has', m.has('hyc'))
        console.log('m.get', m.get('lmx'))
        // m.delete('lmx')
        // console.log('m.get', m.get('lmx'))
    })()

    /* 19. A instanceof B */
    //    （手动实现js方法系列）
    ;(function () {
        return

    //    模拟一个instanceof方法
    //    A是否可以找到B的prototype(B的prototype是否在A的原型链上)
    //    男孩是否可以找到女孩闺房
        function canBoyFind(boy, girl) {
            // boudoir就是女孩的闺房
            var boudoir = girl.prototype
            // room就是男孩现在所在的房间
            var room = boy.__proto__
            while (true){
                // 如果男孩没在任何房间，他也就没有下一把通往另一个房间的钥匙，那他再不会找到女孩的闺房了
                if (room === null){
                    return false
                }
                // 男孩就在女孩闺房，恭喜这对新人
                if (room === boudoir){
                    return true
                }
                // 虽然男孩所在房间不是女孩闺房，但是这里有把通往下一个房间的钥匙(__proto__)
                // 于是男孩走进下一个房间...
                room = room.__proto__
            }
        }


        console.log(Object instanceof Object) // true
        console.log(Function instanceof Function) // true
        console.log(Number instanceof Number) // false
    })()

    /* 20. typeof prototype */
    ;(function () {
        return

        console.log(typeof Function.prototype) // function
        console.log(typeof Object.prototype) // object
        console.log(typeof Number.prototype) // object
    })()

    /* 21. 3行代码实现promise */
    // first
    ;(function () {
        return

        function EasyPromiseEs6(fn) {
            this.then = cb => this.cb = cb
            this.resolve = data => this.cb(data)
            fn(this.resolve)
        }

        function EasyPromise(fn) {
            var that = this
            this.then = function (cb) {
                that.cb = cb
            }
            this.resolve = function (data) {
                that.cb(data)
            }
            fn(this.resolve)
        }
        /*
        *  和直接执行fn的区别，Promise就是
        *    resolve就是fn的一个回调函数；
        *    then就是接收并保存这个回调函数。
        * */

        new EasyPromise(function (resolve) {
            setTimeout(function () {
                console.log('is time')
                resolve('i am data')
            }, 1000)
            console.log('in cb')
        }).then(function (data) {
            console.log('then:', data)
        })
    })()

    // 21. try 1
    ;(function () {
        return

        function EasyPromiseEs6(fn) {
            this.then = cb => this.cb = cb
            this.resolve = data => this.cb(data)
            fn(this.resolve)
        }

        function EasyPromise(fn) {
            var that = this
            // then用来得到“待执行”的函数
            this.then = function (thenCb) {
                that.thenCb = thenCb
                return that
            }
            this.catch = function(catchCb){
                that.catchCb = catchCb
            }
            var thenCb = function(data){
                that.thenCb(data)
            }
            var catchCb = function(data){
                that.catchCb(data)
            }
            fn(thenCb, catchCb)
        }

        new EasyPromise(function (resolve, reject) {
            setTimeout(function () {
                console.log('is time')
                resolve('i am data')
                reject('出错了')
            }, 2000)
            console.log('in cb')
        }).then(function (data) {
            console.log('then:', data)
        }).catch(function (msg) {
            console.log('msg', msg)
        })
    })()

    /* 21. try 2 */
    ;(function () {
        return

        var p = new Promise(function (resolve) {
            setTimeout(function () {
                resolve('时间到')
            }, 1000)
        })
        p.then(function (res) {
            console.log(res)
        })


        // 1.普通执行
        function fn() {
            setTimeout(function () {
                console.log('时间到')
            }, 1000)
        }
        fn()

        // 2.定义回调函数
        function fn() {
            setTimeout(function () {
                cb('时间到')
            }, 1000)
        }
        function cb(data) {
            console.log(data)
        }
        fn(cb)

        /*
        *  3.写一个构造函数：
        *    参数是一个函数： 定义完就会执行
        *    有一个方法then: 里面传递的是回调函数cb
        * */
        function Pro(fn) {

        }

    })()


    /* 22.flat */
    // 扁平化数组，将数组转化为一维数组
    // * 这是一个试验中的功能
    ;(function () {
        return

        const arr = [1,2,[3,[4,5]]]
        // 默认展开一层
        console.log(arr.flat()) // [1,2,3,[4,5]]

        console.log(arr.flat(2)) // [1,2,3,4,5]
        console.log(arr.flat(6)) // [1,2,3,4,5]


    //   利用toString实现扁平化
        var arr2 = [1,[2,[3,[4,[5]]]]]
        var result = arr2.toString().split(',').map(i => +i)

    })()


    /* 23. new操作符的作用 */
    ;(function () {
        return

        // 参考自：
        // 深入理解 new 操作符
        // https://www.cnblogs.com/onepixel/p/5043523.html

        function Animal(name) {
            this.name = name
        }
        var cat = new Animal('贱贱')
        console.log('cat', cat)

        // new伪代码
        /*
            new Animal('cat') = {
                var obj = {};
                obj.__proto__ = Animal.prototype;
                var result = Animal.call(obj,"cat");
                return typeof result === 'object'? result : obj;
            }
        */

        function _new(Fn) {
            var obj = {}
            obj.__proto__ = Fn.prototype
            var args = [...arguments].slice(1)
            var newObj = Fn.call(obj, ...args)
            return typeof newObj === 'object' ? newObj : obj
        }

        var dog = _new(Animal, '轮胎')
        console.log('dog', dog)

        /*
        *  new操作符和构造函数的关系，就好像厕所和上厕所的关系。
        *  构造函数要用new调用，就好比要上厕所就应该去厕所一样。
        *  但是，构造函数可以不用new调用吗？
        *  可以的，这就像一个人在没有厕所的情况下上厕所一样。
        *  同时，拿构造函数当普通函数调用，这是我们不提倡的，就像我们不提倡随地大小便一样。
        *  因为这样会有一定的风险，这就好比随地大小便会污染环境。
        *
        * */
    })()

    /* 23 */
    ;(function () {
        return

        function Person(think) {
            this.think = think
        }
        Person.prototype.say = function () {
            console.log(this.think)
        }

        var x = new Person('好好学习、天天向上')
        x.say() // '好好学习、天天向上'

        /*
        *  new Person('好好学习、天天向上') = {
        *       var baby = {}
        *       // 先天继承
        *       baby.__proto__ = Person.prototype
        *       // 后天塑造
        *       var man = Person.call(baby, '好好学习、天天向上')
        *       return typeof man === 'object' ? man : baby
        *  }
        */

    })()

    /* 24. 一道题 */
    ;(function () {
        return

        function Foo() {
            getName = function () {
                console.log(1)
            }
            return this;
        }

        Foo.getName = function () {
            console.log(2)
        }
        Foo.prototype.getName = function () {
            console.log(3)
        }
        var getName = function () {
            console.log(4)
        }

        function getName() {
            console.log(5)
        }

// ouput:
        Foo.getName(); // 2
        getName(); // 4
        try{
            Foo().getName(); // Uncaught TypeError: Foo(...).getName is not a function
        }catch (e) {
            console.log(e)
        }
        getName(); // 1
        new Foo.getName(); // 2
        new Foo().getName(); // 3. 实例
        new new Foo().getName(); // 3
    })()

    /* 25.module.exports和exports */
    // commonJS
    ;(function () {
        return

        function require(params) {
            // some codes

            var module = {
                exports: {}
            }
            ;(function (module, exports) {

                // yourself code
                yourCode(module, exports)

            })(module, module.exports)
            return module.exports || {}
        }
        var result = require()


        function yourCode(module, exports){
            const obj = {
                title: '大众美男典范'
            }
        //     case1
        //     module.exports = obj // {title: '大众美男典范'}

        //     case2
        //     exports = {school: '清华'} // {}。exports之前保存的是module.exports的引用，这里只是丢失了这个引用。

        //     case3
        //     exports.name = '小明'
        //     exports = {school: '清华'} // {name: '小明'}。这里也是丢失了module.exports的引用，但是在丢失之前对其进行了一些改变。

        //     case4
            exports.name = '小明'
            exports = {school: '清华'}
            module.exports = obj // {title: '大众美男典范'}。前两句过程同上，最后一句又是直接改变了module.exports的值。

        //     case5
        //     module.exports = obj
        //     module.exports.school = '清华' // {title: '大众美男典范', school: '清华'}。最终返回的是module.exports。

            //     case6
            // module.exports = obj
            // exports.school = '清华' // {title: '大众美男典范'}。最终返回的是module.exports。这次是exports被动丢失module.exports的引用（module替换了之前的exports）。
        }



        // 综上，module.exports的方式比较保险。exports真不靠谱。

        console.log('result', result)

    //    module.exports和exports外传
        /*
        *  require之作用，就是module.exports.
        *  把module比作小明，把module.exports比作小明的女朋友；
        *  require之作用，就是得到“小明的女朋友”
        *  而requires，就好比小明最初的女朋友，比如叫小红。
        *  在模块创建之初，小明的女朋友和小红指向同一个对象，即小明的女朋友就是小红。
        *  所以，对小红做什么就相当于对小明的女朋友做什么。
        *  但是，当requires被赋值一个对象，相当于小红找了新对象，小红就不再是小明的对象。
        *  此时，对小红做什么都不重要了，因为我们想得到的是“小明的女朋友”。
        *  同理，当module.exports被赋值一个对象，相当于小明找了新对象，小红也被动的不再是小明的对象。
        *  小红又不重要了。
        *  综上，不忘初心，方得“小明的女朋友”。
        *
        * */
    })()

    /* 26. 构造函数和class的区别*/
    ;(function () {
        return

        // 构造函数：
        function Person() {}
        Person.prototype.say = function (msg) {
            console.log('say:', msg)
        }
        console.log('keys:', Object.keys(Person.prototype)) // ["say"]
        console.log('own:', Object.getOwnPropertyNames(Person.prototype)) // ["constructor", "say"]


        // class:
        class Dog{
            constructor(){}
            say(msg){
                console.log('say:', msg)
            }
        }
        console.log('keys:', Object.keys(Dog.prototype)) // []。 是不可枚举的。
        console.log('own:', Object.getOwnPropertyNames(Dog.prototype)) // ["constructor", "say"]
    })()


    /* 27. class */
    ;(function () {
        return

        class Animal {
            static eat(){
                return 'energy'
            }
        }

        var methodName = 'getAge'; // 类的属性名可以采用表达式
        class Person {

            constructor(name, age){
                this.nameTemp = name || '小明'
                this.age = age || 18
            }
            get name(){
                console.log('do get name')
                return this.nameTemp
            }
            set name(val){
                console.log('do set name:', val)
                this.nameTemp = val
            }
            sayName(){
                console.log('name:', this.name)
            }
            [methodName](){
                console.log('属性名', methodName)
                var v = methodName.split('get')[1].toLowerCase()
                console.log(v, this[v])
            }
            // 通过类来调用，不会被实例继承
            static sayLove(){
                console.log('love ' + Animal.eat())
            }
        }

        var p = new Person('小强')
        p.sayName()
        p.name = '李明'
        p.getAge()
        // p.sayLove() // 报错
        Person.sayLove() // 'love'
        console.log(p.school)
    })()

    /* 27.class中的this */
    ;(function () {
        return

        class Logger{
            constructor(){
                // this.printName = this.printName.bind(this) // 所以要绑定this
            }
            printName(name = '小明'){
                this.print(`name: ${name}`)
            }
            print(text){
                console.log('print:', text)
            }
        }
        var l = new Logger()
        // 情况一：
        // l.printName()

    //    情况二：
        var printName = l.printName
        printName() // 报错
    })()

    /* 28. 非匿名自执行函数函数，函数变量为只读状态，无法修改 */
    ;(function () {
        return

        var foo = 123
        function foo() {
            foo = 'hello'
        }
        // foo() // 报错
        console.log('foo', foo) // '123'

        // 非匿名自执行函数
        var fun = 321
        ;(function fun() {
            fun = 'hi' // fun在函数中只为只读，因此赋值无效
            console.log('fun:', fun) // f fun(){...}
        })()
    })()


    /* 29.防抖 */
    /*
    *  等待结束。
    *
    *  场景：用户输入。
    * */
    ;(function () {
        return

        function debounce(fn, wait, immediate) {
            let timer = null

            return function() {
                let args = arguments
                let context = this

                // 立即触发一次
                if (immediate) {
                    fn.apply(context, args)
                    immediate = false
                }

                if (timer) clearTimeout(timer)
                timer = setTimeout(() => {
                    fn.apply(context, args)
                }, wait)
            }
        }

        var cb = function (e) {
            console.log('e', e)
        }
        document.body.onmousemove = debounce(cb, 3000, true)
    })()

    /* 30. 节流 */
    /*
    *  降低频率。
    *
    *  场景：滚动条事件，resize事件。
    * */
    ;(function () {
        return

        function throttle(fn, wait, immediate) {
            let timer = null

            return function() {
                let args = arguments
                let context = this

                if (immediate) {
                    fn.apply(context, args)
                    immediate = false
                }

                if (!timer) {
                    timer = setTimeout(() => {
                        fn.apply(context, args)
                        timer = null
                    }, wait)
                }
            }
        }
        var cb = function (e) {
            console.log('e', e)
        }
        document.body.onmousemove = throttle(cb, 300)
    })()

    /* 31. 圣杯模型 */
    ;(function () {
        return

        var inherit = (function(c,p){
            var F = function(){};
            return function(c,p){
                F.prototype = p.prototype;
                c.prototype = new F();
                c.uber = p.prototype;
                c.prototype.constructor = c;
            }
        })();
    })()


    /* 32. 函数柯里化 */
    /*
    *  利用闭包特点，通过修改外层函数的参数，返回具有类似但非相同功能的函数。
    * */
    ;(function () {
        return

        const add = function add(x) { // 功能差异区
            return function (y) { // 功能相同区
                return x + y
            }
        }

        const add1 = add(1)
        const add2 = add(2)

        console.log(add1(1))
        console.log(add1(11))
    })()

    /* 33.Object.is() */
    // ES6
    ;(function () {
        return

        console.log(NaN == NaN) // false
        console.log(+0 === -0) // true

        console.log(Object.is(NaN, NaN)) // true
        console.log(Object.is(+0, -0)) // false
    })()

    /* 34. requestAnimationFrame */
    ;(function () {
        return

        /*
        *  根据系统的刷新而执行回调函数。
        *  它能保证回调函数在屏幕每一次刷新间隔中执行一次，这样就不会引起丢帧。
        * */

        var num = 0
        function render() {
            num++
            console.log('num', num)
            if(num < 1000){
                window.requestAnimationFrame(render)
            }
        }
        window.requestAnimationFrame(render)
    })()


    /* 35.一个题 */
    ;(function () {
        // return

        // 什么情况下以下代码输出1？

        let val = 1
        Object.defineProperty(window, 'a', {
            get: function () {
                return val++
            }
        })

        if(a==1 && a==2 && a==3) {
            console.log(111)
        }
    })()



</script>
</body>
</html>